package com.zmn.oms.zmn.business.impl.open;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zmn.base.product.common.dto.product.ProductBaseQuery;
import com.zmn.base.product.common.dto.product.ec.ProductBaseDRO;
import com.zmn.base.product.dubbo.interfaces.product.foreign.ProductForeignListRemoteService;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.constant.StatusConsts;
import com.zmn.common.utils.collection.CollectionUtil;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.http.HttpUtil;
import com.zmn.common.utils.math.MoneyUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.reflect.BeanMapper;
import com.zmn.oms.common.constant.OrderConsts;
import com.zmn.oms.common.constant.OrderStatusConsts;
import com.zmn.oms.common.constant.RedisKeyConsts;
import com.zmn.oms.common.dto.ResultDTO;
import com.zmn.oms.common.exception.OmsBaseException;
import com.zmn.oms.common.utils.SignUtil;
import com.zmn.oms.model.bo.order.OrderBO;
import com.zmn.oms.model.dto.order.open.*;
import com.zmn.oms.model.dto.work.modify.amount.ZsUpdateChannelPrepayAmountDTO;
import com.zmn.oms.model.dto.work.modify.must.ZsCancelDTO;
import com.zmn.oms.model.entity.chan.plat.ChannelInterface;
import com.zmn.oms.model.entity.detail.OrderDetail;
import com.zmn.oms.model.entity.notifylog.OrderNotifyLog;
import com.zmn.oms.model.entity.order.OrderExtend;
import com.zmn.oms.model.entity.product.OrderProduct;
import com.zmn.oms.model.entity.serviceitem.OrderServiceItem;
import com.zmn.oms.model.entity.work.OrderWork;
import com.zmn.oms.model.entity.work.OrderWorkLogChange;
import com.zmn.oms.model.vo.order.open.AvailableTimeslotsVO;
import com.zmn.oms.model.vo.order.open.OrderInfoVO;
import com.zmn.oms.model.vo.order.open.OrderServiceItemVO;
import com.zmn.oms.model.vo.remark.OrderRemarkVO;
import com.zmn.oms.model.vo.work.ZsOrderWorkVO;
import com.zmn.oms.model.vo.work.schedule.OrderScheduleVO;
import com.zmn.oms.zmn.business.interfaces.chan.ChannelInterfaceBService;
import com.zmn.oms.zmn.business.interfaces.open.OpenOrderBService;
import com.zmn.pay.common.constant.PayConsts;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.jsoup.internal.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 类描述：订单接口
 *
 * @author liuying
 * @date 2019/02/18 17:04
 */
@Service
public class OpenOrderBServiceImpl extends OpenOrderBaseBServiceImpl implements OpenOrderBService {

    private static final  String FIELD_EXTRADATA = "extraData";

    // 空跑单金额
    private static final int ONLY_RUN_ORDER_AMOUNT = 3000;

    @Reference(version = com.zmn.base.plat.engine.common.constant.DubboConsts.INTERFACE_VERSION , check = false)
    private ProductForeignListRemoteService productForeignListRemoteService;

    @Autowired
    private ChannelInterfaceBService channelInterfaceBService;

    @Override
    public List<AvailableTimeslotsVO> queryAvailableTimeslots(AvailableTimeslotsQuery availableTimeslotsQuery) throws OmsBaseException {

        com.zmn.common.dto2.ResponseDTO<List<ProductBaseDRO>> responseDTO = null;
        try {
            ProductBaseQuery productDIO = new ProductBaseQuery();
            productDIO.setProductId(availableTimeslotsQuery.getProductId());
            responseDTO = productForeignListRemoteService.listBaseDROByQuery(productDIO);
        } catch (Exception ex) {
            logger.error(ex.getMessage());

        }

        if (responseDTO == null || !responseDTO.isSuccess() || CollectionUtil.isNullOrEmpty(responseDTO.getData())) {
            throw new OmsBaseException(StatusConsts.ERROR_DATA, "产品不存在！");
        }

        // 2、获取预约时间
        List<AvailableTimeslotsVO> timeList = Lists.newArrayList();

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        int max = 7;
        for (int i = 0; i < max; i++) {
            String day = DateUtil.toString(calendar.getTime(), DateUtil.FORMAT_DATE);

            AvailableTimeslotsVO availableTimeslotsVO = new AvailableTimeslotsVO();
            availableTimeslotsVO.setDate(day);
            OrderScheduleVO orderScheduleVO = zsOrderWorkScheduleBService.getCustomerScheduleTimes(day);
            availableTimeslotsVO.setTimeslots(orderScheduleVO.getTimes());
            timeList.add(availableTimeslotsVO);

            calendar.add(Calendar.DAY_OF_YEAR, 1);
        }

        return timeList;
    }

    @Override
    public OrderInfoVO queryOrder(OrderQueryDTO orderQueryDTO, OrderWorkLogChange orderWorkLogChange) throws OmsBaseException {

        // // 获得订单ID
        Long orderId = orderQueryDTO.getOrderId();
        if (NumberUtil.isNullOrZero(orderId)) {
            if (StringUtil.isBlank(orderQueryDTO.getThirdOrderId())) {
                throw new OmsBaseException("参数错误，订单id 和 渠道订单id 均未填写！");
            }

            ZsOrderWorkVO zsOrderWorkVO = super.getChannelOrderByOuterId(orderQueryDTO.getChannelId(), orderQueryDTO.getThirdOrderId());
            if (Objects.isNull(zsOrderWorkVO)) {
                throw new OmsBaseException("订单不存在！");
            }

            orderId = zsOrderWorkVO.getOrderId();
        }

        // 校验渠道订单信息
        OrderWork dbOrderWork = super.requireChannelOrder(orderQueryDTO.getChannelId(), orderId);
        if (dbOrderWork == null) {
            throw new OmsBaseException("订单不存在！");
        }

        OrderDetail dbOrderDetail = orderDetailService.findOrderDetailByKey(dbOrderWork.getOrderId());

        OrderInfoVO orderInfoVO = BeanMapper.map(dbOrderWork, OrderInfoVO.class);

        if (StringUtils.isNotBlank(orderInfoVO.getMasterName())) {
            orderInfoVO.setMasterName(String.format("%s工程师", orderInfoVO.getMasterName().substring(0,1)));
        }

        orderInfoVO.setContactName(StringUtils.defaultIfBlank(dbOrderDetail.getContactName(), dbOrderDetail.getUserName()));
        orderInfoVO.setTelephone(dbOrderDetail.getTelephone());
        orderInfoVO.setAddress(dbOrderDetail.getAddress());
        orderInfoVO.setStreet(dbOrderDetail.getStreet());
        orderInfoVO.setCityId(dbOrderWork.getCityId());
        orderInfoVO.setCityName(dbOrderDetail.getCityName());
        orderInfoVO.setCountyId(dbOrderDetail.getCountyId());
        orderInfoVO.setCountyName(dbOrderDetail.getCountyName());
        orderInfoVO.setLongitude(dbOrderDetail.getLongitude());
        orderInfoVO.setLatitude(dbOrderDetail.getLatitude());
        orderInfoVO.setServCategId(dbOrderWork.getServCategId());
        orderInfoVO.setServCategName(GlobalConsts.getServCategName(dbOrderWork.getServCategId()));

        // 订单状态
        Integer workStatus = orderWorkLogChange == null ? dbOrderWork.getStatus() : orderWorkLogChange.getStatus();
        Integer resultStatus = orderWorkLogChange == null ? dbOrderWork.getResultStatus() : orderWorkLogChange.getResultStatus();
        Integer orderStatus = OrderStatusConsts.getOrderStatus(workStatus, resultStatus);

        orderInfoVO.setStatus(orderStatus);
        orderInfoVO.setStatusName(OrderStatusConsts.getOrderStatusName(orderStatus));

        // 订单取消，取消备注
        if (Objects.equals(OrderStatusConsts.ORDER_STATUS_CANCEL, orderStatus)) {

            OrderRemarkVO orderRemarkVO = orderRemarkBService.findOrderRemarkVO(dbOrderWork.getOrderId(), dbOrderWork.getOrderId(), OrderConsts.ORDER_REMARK_TYPE_CANCEL);
            if (orderRemarkVO != null) {
                orderInfoVO.setCancelRemark(orderRemarkVO.getContent());
            }

            // 订单取消时间
            orderInfoVO.setCancelTime(dbOrderWork.getResultTime());
        }

        // 订单完成，服务项和空跑单判断
        if (Objects.equals(OrderStatusConsts.ORDER_STATUS_COMPLETE, orderStatus)) {

            // 服务项
            List<OrderServiceItem> serviceItemList = zsNormalOrderServiceItemBService.listByOrderId(orderId);
            if (CollectionUtil.isNotNullOrEmpty(serviceItemList)) {
                orderInfoVO.setServiceItems(BeanMapper.mapList(serviceItemList, OrderServiceItemVO.class));
            }

            // 空跑单
            if (Objects.equals(dbOrderWork.getOriginalAmount(),ONLY_RUN_ORDER_AMOUNT)) {
                orderInfoVO.setOnlyRun(GlobalConsts.YES);
            }
        }

        // 查询产品信息
        List<OrderProduct> orderProductList = orderProductService.listOrderProductByOrderId(dbOrderWork.getOrderId());
        if (CollectionUtils.isEmpty(orderProductList)) {
            throw new OmsBaseException("订单错误！");
        }

        OrderProduct orderProduct = orderProductList.get(0);
        orderInfoVO.setProductId(orderProduct.getProductId());
        orderInfoVO.setProductName(orderProduct.getProductName());
        orderInfoVO.setProductCount(orderProduct.getNumber());

        // 外部订单ID
        orderInfoVO.setThirdOrderId(dbOrderWork.getOuterId());

        // 扩展信息
        OrderExtend orderExtend = orderExtendService.findOrderExtendByOrderId(dbOrderWork.getOrderId());
        if (orderExtend != null) {
            if (StringUtils.isNotBlank(orderExtend.getOuterData())) {
                JSONObject jsonObject = JSON.parseObject(orderExtend.getOuterData());
                if (jsonObject.containsKey(FIELD_EXTRADATA)) {
                    orderInfoVO.setExtraData(jsonObject.getString(FIELD_EXTRADATA));
                }
            }

            // 厂商订单，返回产品照片信息
            if (Objects.equals(OrderConsts.ORDER_SOURCE_TYPE_FACTORY, dbOrderWork.getSourceType())
                    && StringUtils.isNotBlank(orderExtend.getImageSrc())) {
                orderInfoVO.setProductPictureUrls(orderExtend.getImageSrc());
            }
        }

        return orderInfoVO;
    }

    @Override
    public OrderInfoVO queryOrder(OrderQueryDTO orderQueryDTO) throws OmsBaseException {
        return this.queryOrder(orderQueryDTO, null);
    }

    @Override
    public ResultDTO pushOrderInfo(OrderWorkLogChange orderWorkLogChange) throws OmsBaseException {
        Optional<ChannelInterface> optional = channelInterfaceBService.findByChannelId(orderWorkLogChange.getChannelId());
        if (!optional.isPresent()) {
            return ResultDTO.success();
        }

        ChannelInterface channelInterface = optional.get();
        if (channelInterface.getStatus() != null && !Objects.equals(GlobalConsts.YES, channelInterface.getStatus())) {
            return ResultDTO.success();
        }

        if (StringUtils.isBlank(channelInterface.getInterfaceStatusUrl())) {
            return ResultDTO.success();
        }

        OrderQueryDTO orderQueryDTO = new OrderQueryDTO();
        orderQueryDTO.setOrderId(orderWorkLogChange.getOrderId());
        orderQueryDTO.setChannelId(orderWorkLogChange.getChannelId());

        OrderInfoVO orderInfoVO = this.queryOrder(orderQueryDTO, orderWorkLogChange);
        Map<String, String> params = Maps.newHashMap();
        params.put("appId", String.valueOf(channelInterface.getChannelId()));
        params.put("jsonData", JSON.toJSONString(orderInfoVO));
        params.put("version", "1");
        params.put("timestamp", String.valueOf((System.currentTimeMillis() / 1000)));
        params.put("sign", SignUtil.signMd5(params, channelInterface.getAppSecret()));

        StringBuilder sb = new StringBuilder();
        for (String paramName : params.keySet()) {
            sb.append("&").append(paramName).append("=").append(params.get(paramName));
        }
        logger.info("{}?{}", channelInterface.getInterfaceStatusUrl(), sb.substring(1));

        String response = HttpUtil.post(channelInterface.getInterfaceStatusUrl(), params);
        logger.info(response);
        
        return this.toResultDTO(response, OrderConsts.ORDER_NOTIFY_TYPE_CHANGE, orderWorkLogChange, orderInfoVO);
    }

    @Override
    public ResultDTO pushNewOrderInfo(OrderWorkLogChange orderWorkLogChange) throws OmsBaseException {
        Optional<ChannelInterface> optional = channelInterfaceBService.findByChannelId(orderWorkLogChange.getChannelId());
        if (!optional.isPresent()) {
            return ResultDTO.success();
        }

        ChannelInterface channelInterface = optional.get();
        if (channelInterface.getStatus() != null && !Objects.equals(GlobalConsts.YES, channelInterface.getStatus())) {
            return ResultDTO.success();
        }

        if (StringUtils.isBlank(channelInterface.getInterfaceUrl())) {
            return ResultDTO.success();
        }

        OrderQueryDTO orderQueryDTO = new OrderQueryDTO();
        orderQueryDTO.setOrderId(orderWorkLogChange.getOrderId());
        orderQueryDTO.setChannelId(orderWorkLogChange.getChannelId());

        OrderInfoVO orderInfoVO = this.queryOrder(orderQueryDTO, orderWorkLogChange);
        Map<String, String> params = Maps.newHashMap();
        params.put("appId", String.valueOf(channelInterface.getChannelId()));
        params.put("jsonData", JSON.toJSONString(orderInfoVO));
        params.put("version", "1");
        params.put("timestamp", String.valueOf((System.currentTimeMillis() / 1000)));
        params.put("sign", SignUtil.signMd5(params, channelInterface.getAppSecret()));

        StringBuilder sb = new StringBuilder();
        for (String paramName : params.keySet()) {
            sb.append("&").append(paramName).append("=").append(params.get(paramName));
        }
        logger.info("{}?{}", channelInterface.getInterfaceUrl(), sb.substring(1));

        String response = HttpUtil.post(channelInterface.getInterfaceUrl(), params);
        logger.info(response);

        return this.toResultDTO(response, OrderConsts.ORDER_NOTIFY_TYPE_NEW, orderWorkLogChange, orderInfoVO);
    }

    /**
     * 返回结果转化
     *
     * @param response
     * @param notifyType
     * @param orderWorkLogChange
     * @param orderInfoVO
     * @return
     */
    private ResultDTO toResultDTO(String response, int notifyType, OrderWorkLogChange orderWorkLogChange, OrderInfoVO orderInfoVO) {
        OrderNotifyLog orderNotifyLog = new OrderNotifyLog();
        orderNotifyLog.setOrderId(orderWorkLogChange.getOrderId());
        orderNotifyLog.setOrderStatus(orderWorkLogChange.getStatus());
        orderNotifyLog.setOrderResultStatus(orderWorkLogChange.getResultStatus());
        orderNotifyLog.setNotifyStatus(String.valueOf(orderInfoVO.getStatus()));
        orderNotifyLog.setType(notifyType);
        orderNotifyLog.setNotifyTime(DateUtil.getNow());
        orderNotifyLog.setMqMsgKey(orderWorkLogChange.getMqMsgKey());

        ResultDTO resultDTO = ResultDTO.fail(StatusConsts.ERROR_PARAMS, "返回数据格式错误");
        if (StringUtils.isNotBlank(response)) {
            try {
                ResultDTO result = JSON.parseObject(response, ResultDTO.class);
                resultDTO.setStatus(result.getStatus());
                resultDTO.setStatusBool(result.isStatusBool());
                resultDTO.setMessage(result.getMessage());
            } catch (Exception ex) {
            }
        }

        resultDTO.setData(orderNotifyLog);
        return resultDTO;
    }

    @Override
    public OrderBO createOrder(CreateOrderDTO createOrderDTO) throws OmsBaseException {
        logger.info("[Open-Order] CreateOrderDTO=[{}]",  JSON.toJSONString(createOrderDTO));

        if (isDuplicateClue(createOrderDTO.getChannelId(), createOrderDTO.getThirdOrderId())) {
            throw new OmsBaseException(StatusConsts.ERROR_DATA, "订单已存在");
        }

        // 处理成标准的接口录单数据
        super.preparedForCreateOrderData(createOrderDTO);

        return proxyOrderBService.createOrder(createOrderDTO);
    }

    @Override
    public void channelPrepayOrder(PayOrderDTO payOrderDTO) throws Exception {

        // 校验渠道订单信息
        OrderWork dbOrderWork = super.requireChannelOrder(payOrderDTO.getChannelId(), payOrderDTO.getOrderId());

        // 订单状态非进行中，或者维修完成
        if (!Objects.equals(OrderStatusConsts.WORK_RESULT_DOING, dbOrderWork.getResultStatus()) || dbOrderWork.getCompleteTime() != null) {
            throw new OmsBaseException(StatusConsts.ERROR_DATA, "订单已完结，请勿支付");
        }

        if (Objects.equals(dbOrderWork.getChannelPrepayStatus(), PayConsts.PAY_STATUS_DONE) || Objects.equals(dbOrderWork.getPayStatus(), PayConsts.PAY_STATUS_DONE)) {
            throw new OmsBaseException(StatusConsts.ERROR_DATA, "订单已支付！");
        }

        OrderWork orderWork = orderWorkService.findOrderWorkByKey(payOrderDTO.getOrderId(), payOrderDTO.getOrderId());
        if (!Objects.equals(orderWork.getChannelPrepayAmount(), payOrderDTO.getPayAmount())) {
            throw new OmsBaseException(StatusConsts.ERROR_DATA, "订单支付金额错误！");
        }

        // 渠道预付
        ZsUpdateChannelPrepayAmountDTO channelPrepayAmountDTO = new ZsUpdateChannelPrepayAmountDTO();

        channelPrepayAmountDTO.setOperatorRemark(payOrderDTO.getRemark());
        channelPrepayAmountDTO.setOperatorLogRemark("渠道支付：" + MoneyUtil.parseToYuan(payOrderDTO.getPayAmount()));

        // 操作人
        channelPrepayAmountDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
        channelPrepayAmountDTO.setOperatorId((long) payOrderDTO.getChannelId());
        channelPrepayAmountDTO.setOperator("系统");

        channelPrepayAmountDTO.setOrderId(payOrderDTO.getOrderId());
        channelPrepayAmountDTO.setWorkId(payOrderDTO.getOrderId());
        channelPrepayAmountDTO.setChannelPrepayAmount(payOrderDTO.getPayAmount());
        zsNormalMasterWorkBService.saveChannelPrepayAmountPay(channelPrepayAmountDTO);
    }

    @Override
    public void cancelOrder(CancelOrderDTO cancelOrderDTO) throws OmsBaseException {

        // 校验渠道订单信息
        OrderWork dbOrderWork = super.requireChannelOrder(cancelOrderDTO.getChannelId(), cancelOrderDTO.getOrderId());

        // 订单状态为弃单或已取消
        if (Objects.equals(OrderStatusConsts.WORK_RESULT_DISCARD, dbOrderWork.getResultStatus()) || Objects.equals(OrderStatusConsts.WORK_RESULT_FAIL, dbOrderWork.getResultStatus())) {
            throw new OmsBaseException(StatusConsts.ERROR_DATA, "订单已取消，不可重复取消");
        }

        // 订单状态为已完成，或者工程师维修完成
        if (Objects.equals(OrderStatusConsts.WORK_RESULT_SUCCESS, dbOrderWork.getResultStatus()) || dbOrderWork.getCompleteTime() != null) {
            throw new OmsBaseException(StatusConsts.ERROR_DATA, "订单已完成，不可取消");
        }

        ZsCancelDTO zsCancelDTO = BeanMapper.map(cancelOrderDTO, ZsCancelDTO.class);

        // 渠道申请取消，默认不校验是否存在待审核的取消申请 2022.07.22 by lhl
        zsCancelDTO.setAgreeCancel(GlobalConsts.YES);
        // 操作人为渠道
        zsCancelDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
        zsCancelDTO.setOperatorId((long) cancelOrderDTO.getChannelId());
        zsCancelDTO.setOperator("渠道");

        zsCancelDTO.setOrderId(cancelOrderDTO.getOrderId());
        zsCancelDTO.setWorkId(cancelOrderDTO.getOrderId());
        zsCancelDTO.setContent(cancelOrderDTO.getRemark());
        zsNormalWorkBService.saveCancel(zsCancelDTO);
    }

    /**
     * 判断是否是重复的新单消息, 如果是重复消息，返回true
     * @param outerId
     * @return
     */
    private boolean isDuplicateClue(Integer channelId, String outerId) throws OmsBaseException {
        // 没提供渠道单号，默认不重复
        if (StringUtils.isBlank(outerId)) {
            return false;
        }
        /*
         * 新单消息去重需要做两方面处理：
         *  1. 订单在接单后几天内，还是有可能推该单的新单消息，针对这种情况，通过outerId查看数据库中是否有该订单，判断其是否为重复；
         *  2. 第一次收到新单消息时，有可能在间隔不超过一秒的时间内，收到该单的重复推送消息，此时该订单的第一条消息还没有入库，所以
         *      第二条消息在采用1中的去重逻辑时无法识别为重复消息，需要采用redis计数器判断该订单在5分钟内是否重复收到过，得出是否为
         *      重复消息；
         */
        // 方案1去重
        ZsOrderWorkVO orderWorkVO = super.getChannelOrderByOuterId(channelId, outerId);
        if (orderWorkVO != null) {
            return true;
        }

        // 方案2去重
        String redisKey = String.format(RedisKeyConsts.CHANNEL_CLUE_ID, String.valueOf(channelId), outerId);
        Long num = redisManager.incr(redisKey);

        if (num.intValue() > 1) {
            return true;
        }

        // 设置自动过期，清除Redis占用
        redisManager.expire(redisKey, RedisKeyConsts.CHANNEL_CLUE_ID_EXPIRE_SECONDS);

        return false;
    }
}
